import pyautogui
import tkinter as tk
from PIL import ImageGrab
import colorsys
import math
import numpy as np


class YellowDetector:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("黄色检测器-环状区域采样版")

        # 定义黄色的HSV阈值范围
        self.lower_yellow = np.array([20, 50, 50])  # 黄色下限 (H:20°, S:50%, V:50%)
        self.upper_yellow = np.array([40, 255, 255])  # 黄色上限 (H:40°, S:100%, V:100%)

        # 屏幕中心和采样参数
        self.screen_width, self.screen_height = pyautogui.size()
        self.center_x = self.screen_width // 2
        self.center_y = self.screen_height // 2

        # 环形采样参数
        self.ring_radius = 12  # 采样环形区域半径(像素)
        self.sample_points = 12  # 采样点数
        self.detecting = False
        self.detection_threshold = 0.5  # 黄色检测阈值 (0-1)

        # GUI布局
        self.setup_ui()

    def setup_ui(self):
        # 状态显示
        self.status_label = tk.Label(self.root,
                                     text="黄色检测器已就绪\n可调整采样参数并开始检测",
                                     font=('Arial', 12))
        self.status_label.pack(pady=10)

        # 阈值范围显示
        range_frame = tk.Frame(self.root)
        range_frame.pack(fill=tk.X, padx=20, pady=5)

        tk.Label(range_frame, text="黄色范围:").pack(side=tk.LEFT)

        # 显示黄色阈值范围
        lower_text = f"H:{self.lower_yellow[0]}° S:{self.lower_yellow[1]}% V:{self.lower_yellow[2]}%"
        upper_text = f"H:{self.upper_yellow[0]}° S:{self.upper_yellow[1]}% V:{self.upper_yellow[2]}%"
        range_text = f"{lower_text} 到 {upper_text}"

        self.range_label = tk.Label(range_frame, text=range_text, fg="#FFCC00", font=('Arial', 10, 'bold'))
        self.range_label.pack(side=tk.LEFT, padx=10)

        # 采样参数控制面板
        param_frame = tk.Frame(self.root)
        param_frame.pack(fill=tk.X, padx=20, pady=5)

        # 采样半径设置
        tk.Label(param_frame, text="采样半径:").grid(row=0, column=0, sticky='e')
        self.radius_entry = tk.Entry(param_frame, width=5)
        self.radius_entry.insert(0, str(self.ring_radius))
        self.radius_entry.grid(row=0, column=1, sticky='w')

        # 采样点数设置
        tk.Label(param_frame, text="采样点数:").grid(row=0, column=2, sticky='e')
        self.points_entry = tk.Entry(param_frame, width=5)
        self.points_entry.insert(0, str(self.sample_points))
        self.points_entry.grid(row=0, column=3, sticky='w')

        # 检测阈值设置
        tk.Label(param_frame, text="检测阈值:").grid(row=0, column=4, sticky='e')
        self.threshold_entry = tk.Entry(param_frame, width=5)
        self.threshold_entry.insert(0, str(self.detection_threshold))
        self.threshold_entry.grid(row=0, column=5, sticky='w')

        tk.Label(param_frame, text="(0-1)").grid(row=0, column=6, sticky='w')

        # 功能按钮区域
        button_frame = tk.Frame(self.root)
        button_frame.pack(pady=10)

        tk.Button(button_frame, text="开始检测",
                  command=self.toggle_detection, bg="#4CAF50", fg="white").pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="停止检测",
                  command=self.stop_detection, bg="#F44336", fg="white").pack(side=tk.LEFT, padx=5)

        # 提示信息
        self.info_label = tk.Label(self.root,
                                   text="等待启动... | Ctrl+C 退出",
                                   font=('Arial', 10))
        self.info_label.pack(pady=5)

        # 环状采样点显示（可视化采样点）
        self.canvas_frame = tk.Frame(self.root)
        self.canvas_frame.pack(pady=5)
        self.canvas = tk.Canvas(self.canvas_frame, width=120, height=120, bg="#333333", highlightthickness=0)
        self.canvas.pack()

        # 绘制初始环形采样点
        self.draw_sample_points()

        # 绑定退出快捷键
        self.root.bind('<Control-c>', lambda event: self.root.destroy())

    def stop_detection(self):
        self.detecting = False
        self.status_label.config(text="检测已停止")
        self.info_label.config(text="检测已停止 | Ctrl+C 退出")
        self.root.config(bg="SystemButtonFace")
        self.canvas.delete("ring")  # 清除检测状态
        self.canvas.itemconfig("sample_point", fill="gray")  # 恢复采样点颜色
        # 重置所有采样点为灰色
        points_num = self.get_valid_points_num()
        for i in range(points_num):
            self.canvas.itemconfig(f"point_{i}", fill="gray")

    def get_valid_radius(self):
        """获取有效的半径值（如果输入无效则使用默认值）"""
        try:
            radius = int(self.radius_entry.get())
            return max(1, min(radius, 100))  # 限制在1-100范围内
        except ValueError:
            return self.ring_radius

    def get_valid_points_num(self):
        """获取有效的采样点数（如果输入无效则使用默认值）"""
        try:
            points_num = int(self.points_entry.get())
            return max(3, min(points_num, 24))  # 限制在3-24范围内
        except ValueError:
            return self.sample_points

    def get_sample_points(self):
        """获取环形采样点坐标"""
        radius = self.get_valid_radius()
        points_num = self.get_valid_points_num()

        points = []
        for i in range(points_num):
            angle = 2 * math.pi * i / points_num
            x = self.center_x + radius * math.cos(angle)
            y = self.center_y + radius * math.sin(angle)
            points.append((int(x), int(y)))
        return points

    def draw_sample_points(self):
        """在画布上绘制环形采样点"""
        self.canvas.delete("all")
        center_x, center_y = 60, 60  # 画布中心

        # 获取半径和点数
        radius = self.get_valid_radius()
        points_num = self.get_valid_points_num()

        # 计算缩放后的半径（最大50像素）
        scaled_radius = min(50, radius * 2.5)  # 按比例缩放

        # 绘制环形
        self.canvas.create_oval(center_x - scaled_radius, center_y - scaled_radius,
                                center_x + scaled_radius, center_y + scaled_radius,
                                outline="gray", width=2, tags="ring")

        # 绘制采样点
        for i in range(points_num):
            angle = 2 * math.pi * i / points_num
            x = center_x + scaled_radius * math.cos(angle)
            y = center_y + scaled_radius * math.sin(angle)
            self.canvas.create_oval(x - 4, y - 4, x + 4, y + 4,
                                    fill="gray", outline="", tags=("sample_point", f"point_{i}"))

        # 绘制圆心标记
        self.canvas.create_oval(center_x - 2, center_y - 2, center_x + 2, center_y + 2, fill="red", outline="")

    def get_color_at_point(self, point):
        """获取指定位置的颜色"""
        try:
            x, y = point
            # 单点截图效率更高
            bbox = (x, y, x + 1, y + 1)
            region = ImageGrab.grab(bbox=bbox)
            return region.getpixel((0, 0))
        except Exception as e:
            print(f"采样错误: {e}")
            return (0, 0, 0)

    def is_yellow(self, rgb):
        """检查颜色是否在黄色范围内"""
        # 将RGB转换为0-255整数
        r, g, b = [min(max(c, 0), 255) for c in rgb]

        # 转换到HSV (H:0-360, S:0-100, V:0-100)
        r, g, b = r / 255.0, g / 255.0, b / 255.0
        h, s, v = colorsys.rgb_to_hsv(r, g, b)
        h, s, v = h * 360, s * 100, v * 100

        # 检查是否在黄色范围内
        return (self.lower_yellow[0] <= h <= self.upper_yellow[0] and
                self.lower_yellow[1] <= s <= self.upper_yellow[1] and
                self.lower_yellow[2] <= v <= self.upper_yellow[2])

    def toggle_detection(self):
        """切换检测状态"""
        self.detecting = not self.detecting
        if self.detecting:
            self.status_label.config(text="检测运行中 - 正在检测黄色")
            self.info_label.config(text="检测运行中 | Ctrl+C 退出")
            # 启动颜色更新循环
            self.root.after(0, self.update_color)
        else:
            self.stop_detection()

    def update_color(self):
        try:
            if not self.detecting:
                return

            # 获取环形采样点
            points = self.get_sample_points()

            # 更新阈值（确保在0-1范围内）
            try:
                threshold = float(self.threshold_entry.get() or self.detection_threshold)
                self.detection_threshold = max(0.01, min(1.0, threshold))  # 限制在0.01-1.0范围内
            except ValueError:
                self.detection_threshold = 0.5

            # 重新绘制采样点
            self.draw_sample_points()

            # 检测黄色点
            yellow_count = 0
            for i, point in enumerate(points):
                rgb = self.get_color_at_point(point)
                is_yellow = self.is_yellow(rgb)

                if is_yellow:
                    yellow_count += 1
                    self.canvas.itemconfig(f"point_{i}", fill="#FFCC00")  # 黄色
                else:
                    self.canvas.itemconfig(f"point_{i}", fill="#3498DB")  # 蓝色

            # 计算黄色比例
            total_points = len(points)
            yellow_ratio = yellow_count / total_points if total_points > 0 else 0

            # 更新UI反馈
            if yellow_ratio >= self.detection_threshold:
                self.root.config(bg="#FFCC00")
                status_text = f"★ 检测到黄色区域！ ★\n黄色比例: {yellow_ratio:.2f} (阈值: {self.detection_threshold:.2f})"
                self.info_label.config(text=status_text)
                self.canvas.itemconfig("ring", outline="#FFCC00")  # 黄色环
            else:
                self.root.config(bg="SystemButtonFace")
                status_text = (f"检测中... 黄色比例: {yellow_ratio:.2f}/{self.detection_threshold:.2f} "
                               f"({yellow_count}/{total_points} 点黄色)")
                self.info_label.config(text=status_text)
                self.canvas.itemconfig("ring", outline="gray")  # 灰色环

            # 更新采样点状态
            status_text = (f"黄色检测中 | 半径: {self.get_valid_radius()}px | "
                           f"点数: {len(points)} | "
                           f"阈值: {self.detection_threshold:.2f}")
            self.status_label.config(text=status_text)

            # 继续检测
            self.root.after(50, self.update_color)
        except Exception as e:
            print(f"更新错误: {e}")
            self.root.after(50, self.update_color)

    def run(self):
        self.root.mainloop()


if __name__ == "__main__":
    detector = YellowDetector()
    try:
        detector.run()
    except KeyboardInterrupt:
        print("程序已安全退出")